package maple.thelittleredbook.recommendation.service;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import maple.thelittleredbook.recommendation.entity.ContentFeature;
import maple.thelittleredbook.recommendation.mapper.ContentFeatureMapper;
import maple.thelittleredbook.recommendation.mapper.RecommendationUserBehaviorMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;

/**
 * 用户画像更新服务
 * 专门负责基于用户行为更新用户画像，包含冷却期机制
 */
@Slf4j
@Service
public class UserProfileUpdateService {

    private final RecommendationUserBehaviorMapper userBehaviorMapper;
    private final ContentFeatureMapper contentFeatureMapper;

    // 行为权重配置 - 不同行为对用户画像的影响程度
    @Value("${user-profile.behavior-weight.view}")
    private double viewWeight;

    @Value("${user-profile.behavior-weight.like}")
    private double likeWeight;

    @Value("${user-profile.behavior-weight.unlike}")
    private double unlikeWeight;

    @Value("${user-profile.behavior-weight.comment}")
    private double commentWeight;

    @Value("${user-profile.behavior-weight.dislike}")
    private double dislikeWeight;

    // 冷却时间配置
    @Value("${user-profile.behavior-cooldown.view}")
    private String viewCooldown;

    @Value("${user-profile.behavior-cooldown.like}")
    private String likeCooldown;

    @Value("${user-profile.behavior-cooldown.unlike}")
    private String unlikeCooldown;

    @Value("${user-profile.behavior-cooldown.comment}")
    private String commentCooldown;

    // dislike 不需要冷却时间，因为每个内容只能执行一次，唯一性检测在用户行为模块处理

    @Autowired
    public UserProfileUpdateService(RecommendationUserBehaviorMapper userBehaviorMapper,
                                    ContentFeatureMapper contentFeatureMapper) {
        this.userBehaviorMapper = userBehaviorMapper;
        this.contentFeatureMapper = contentFeatureMapper;
    }

    /**
     * 基于浏览行为更新用户画像
     *
     * @param userId 用户ID
     * @param contentId 内容ID
     * @return 是否更新成功
     */
    public boolean updateProfileByView(Long userId, Long contentId) {
        return updateProfileByBehavior(userId, contentId, "VIEW", viewWeight, viewCooldown);
    }

    /**
     * 基于点赞行为更新用户画像
     *
     * @param userId 用户ID
     * @param contentId 内容ID
     * @return 是否更新成功
     */
    public boolean updateProfileByLike(Long userId, Long contentId) {
        return updateProfileByBehavior(userId, contentId, "LIKE", likeWeight, likeCooldown);
    }

    /**
     * 基于取消点赞行为更新用户画像
     *
     * @param userId 用户ID
     * @param contentId 内容ID
     * @return 是否更新成功
     */
    public boolean updateProfileByUnlike(Long userId, Long contentId) {
        return updateProfileByBehavior(userId, contentId, "UNLIKE", unlikeWeight, unlikeCooldown);
    }

    /**
     * 基于评论行为更新用户画像
     *
     * @param userId 用户ID
     * @param contentId 内容ID
     * @return 是否更新成功
     */
    public boolean updateProfileByComment(Long userId, Long contentId) {
        return updateProfileByBehavior(userId, contentId, "COMMENT", commentWeight, commentCooldown);
    }

    /**
     * 基于踩行为更新用户画像
     * 注意：dislike 对每个内容只能执行一次
     *
     * @param userId 用户ID
     * @param contentId 内容ID
     * @return 是否更新成功
     */
    public boolean updateProfileByDislike(Long userId, Long contentId) {
        try {
            log.debug("开始处理踩行为画像更新，用户ID: {}, 内容ID: {}", userId, contentId);

            // 1. 检查是否已经对该内容点踩过（检查用户行为记录）
            int dislikeCount = userBehaviorMapper.countUserBehaviorByType(userId, contentId, "DISLIKE");
            if (dislikeCount > 0) {
                log.debug("用户已对该内容点踩过，跳过画像更新，用户ID: {}, 内容ID: {}", userId, contentId);
                return false;
            }

            // 2. 获取内容特征向量
            ContentFeature contentFeature = contentFeatureMapper.selectCompletedFeatureByContentId(contentId);
            if (contentFeature == null) {
                log.debug("内容特征不存在或未完成分析，跳过用户画像更新，内容ID: {}", contentId);
                return false;
            }

            // 3. 更新画像（第一次点踩）
            boolean updated = updateUserProfileVector(userId, contentFeature, dislikeWeight);

            if (updated) {
                log.debug("踩行为画像更新成功，用户ID: {}, 内容ID: {}, 权重: {}",
                        userId, contentId, dislikeWeight);
            } else {
                log.warn("踩行为画像更新失败，可能用户画像不存在，用户ID: {}", userId);
            }

            return updated;

        } catch (Exception e) {
            log.error("踩行为更新用户画像失败，用户ID: {}, 内容ID: {}, 权重: {}",
                    userId, contentId, dislikeWeight, e);
            return false;
        }
    }

    /**
     * 统一的画像更新逻辑
     *
     * @param userId 用户ID
     * @param contentId 内容ID
     * @param behaviorType 行为类型
     * @param behaviorWeight 行为权重
     * @param cooldownConfig 冷却时间配置
     * @return 是否更新成功
     */
    private boolean updateProfileByBehavior(Long userId, Long contentId, String behaviorType,
                                            double behaviorWeight, String cooldownConfig) {
        try {
            log.debug("开始处理画像更新，用户ID: {}, 内容ID: {}, 行为类型: {}", userId, contentId, behaviorType);

            // 1. 检查冷却期
            if (!shouldUpdateProfile(userId, contentId, behaviorType, cooldownConfig)) {
                log.debug("行为在冷却期内，跳过画像更新，用户ID: {}, 内容ID: {}, 行为类型: {}",
                        userId, contentId, behaviorType);
                return false;
            }

            // 2. 获取内容特征向量
            ContentFeature contentFeature = contentFeatureMapper.selectCompletedFeatureByContentId(contentId);
            if (contentFeature == null) {
                log.debug("内容特征不存在或未完成分析，跳过用户画像更新，内容ID: {}", contentId);
                return false;
            }

            // 3. 计算加权特征向量并更新画像
            boolean updated = updateUserProfileVector(userId, contentFeature, behaviorWeight);

            if (updated) {
                log.debug("画像更新成功，用户ID: {}, 内容ID: {}, 行为类型: {}, 权重: {}",
                        userId, contentId, behaviorType, behaviorWeight);
            } else {
                log.warn("画像更新失败，可能用户画像不存在，用户ID: {}", userId);
            }

            return updated;

        } catch (Exception e) {
            log.error("更新用户画像失败，用户ID: {}, 内容ID: {}, 行为类型: {}, 权重: {}",
                    userId, contentId, behaviorType, behaviorWeight, e);
            return false;
        }
    }

    /**
     * 检查是否应该更新用户画像（冷却期检查）
     *
     * @param userId 用户ID
     * @param contentId 内容ID
     * @param behaviorType 行为类型
     * @param cooldownConfig 冷却时间配置
     * @return true-可以更新画像，false-在冷却期内
     */
    private boolean shouldUpdateProfile(Long userId, Long contentId, String behaviorType, String cooldownConfig) {
        try {
            LocalDateTime cooldownBoundary = LocalDateTime.now().minus(parseDuration(cooldownConfig));

            // 检查冷却期内是否有同类行为
            int recentCount = userBehaviorMapper.countRecentBehavior(userId, contentId, behaviorType, cooldownBoundary);

            return recentCount == 0; // 冷却期内没有同类行为才更新画像

        } catch (Exception e) {
            log.error("检查画像更新条件失败，用户ID: {}, 内容ID: {}, 行为类型: {}", userId, contentId, behaviorType, e);
            return false; // 异常时保守处理，跳过画像更新
        }
    }

    /**
     * 更新用户画像向量
     * 使用线性分类器的向量加法原理
     *
     * @param userId 用户ID
     * @param contentFeature 内容特征
     * @param behaviorWeight 行为权重
     * @return 是否更新成功
     */
    private boolean updateUserProfileVector(Long userId, ContentFeature contentFeature, double behaviorWeight) {
        try {
            // 计算加权特征向量（内容向量 * 行为权重）
            BigDecimal weightDecimal = BigDecimal.valueOf(behaviorWeight);
            BigDecimal[] weightedVector = {
                    multiply(contentFeature.getDim1(), weightDecimal),
                    multiply(contentFeature.getDim2(), weightDecimal),
                    multiply(contentFeature.getDim3(), weightDecimal),
                    multiply(contentFeature.getDim4(), weightDecimal),
                    multiply(contentFeature.getDim5(), weightDecimal),
                    multiply(contentFeature.getDim6(), weightDecimal),
                    multiply(contentFeature.getDim7(), weightDecimal),
                    multiply(contentFeature.getDim8(), weightDecimal)
            };

            // 原子性更新用户画像（向量加法）
            int updateCount = userBehaviorMapper.updateUserProfileByVector(
                    userId,
                    weightedVector[0], weightedVector[1], weightedVector[2], weightedVector[3],
                    weightedVector[4], weightedVector[5], weightedVector[6], weightedVector[7]
            );

            return updateCount > 0;

        } catch (Exception e) {
            log.error("更新用户画像向量失败，用户ID: {}, 行为权重: {}", userId, behaviorWeight, e);
            return false;
        }
    }

    /**
     * 安全的BigDecimal乘法，处理null值
     */
    private BigDecimal multiply(BigDecimal value, BigDecimal multiplier) {
        if (value == null) {
            return BigDecimal.ZERO;
        }
        return value.multiply(multiplier);
    }

    /**
     * 解析时间配置字符串为Duration对象
     * 支持格式：1d, 4h, 30m, 60s
     */
    private Duration parseDuration(String durationStr) {
        if (durationStr == null || durationStr.trim().isEmpty()) {
            return Duration.ofHours(1); // 默认1小时
        }

        String trimmed = durationStr.trim().toLowerCase();

        try {
            if (trimmed.endsWith("d")) {
                int days = Integer.parseInt(trimmed.substring(0, trimmed.length() - 1));
                return Duration.ofDays(days);
            } else if (trimmed.endsWith("h")) {
                int hours = Integer.parseInt(trimmed.substring(0, trimmed.length() - 1));
                return Duration.ofHours(hours);
            } else if (trimmed.endsWith("m")) {
                int minutes = Integer.parseInt(trimmed.substring(0, trimmed.length() - 1));
                return Duration.ofMinutes(minutes);
            } else if (trimmed.endsWith("s")) {
                int seconds = Integer.parseInt(trimmed.substring(0, trimmed.length() - 1));
                return Duration.ofSeconds(seconds);
            } else {
                // 默认当作小时处理
                int hours = Integer.parseInt(trimmed);
                return Duration.ofHours(hours);
            }
        } catch (NumberFormatException e) {
            log.warn("解析冷却时间配置失败，使用默认值1小时: {}", durationStr, e);
            return Duration.ofHours(1);
        }
    }

    /**
     * 批量更新用户画像（可用于定时任务等场景）
     *
     * @param userId 用户ID
     * @param behaviorUpdates 行为更新列表
     * @return 成功更新的数量
     */
    public int batchUpdateProfile(Long userId, java.util.List<BehaviorUpdate> behaviorUpdates) {
        int successCount = 0;
        for (BehaviorUpdate update : behaviorUpdates) {
            boolean success = updateProfileByBehavior(
                    userId,
                    update.getContentId(),
                    update.getBehaviorType(),
                    update.getWeight(),
                    update.getCooldown()
            );
            if (success) {
                successCount++;
            }
        }
        return successCount;
    }

    /**
     * 行为更新参数类
     */
    @Getter
    public static class BehaviorUpdate {
        // Getters
        private final Long contentId;
        private final String behaviorType;
        private final double weight;
        private final String cooldown;

        // 构造函数
        public BehaviorUpdate(Long contentId, String behaviorType, double weight, String cooldown) {
            this.contentId = contentId;
            this.behaviorType = behaviorType;
            this.weight = weight;
            this.cooldown = cooldown;
        }

    }
}